package com.atan.xiaohongshu.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.atan.framework.common.enums.DeletedEnum;
import com.atan.framework.common.enums.StatusEnum;
import com.atan.framework.common.exception.BizException;
import com.atan.framework.common.response.Response;
import com.atan.framework.common.util.JsonUtils;
import com.atan.xiaohongshu.auth.constant.RedisConstants;
import com.atan.xiaohongshu.auth.constant.RoleConstants;
import com.atan.xiaohongshu.auth.domain.dataobject.RoleDO;
import com.atan.xiaohongshu.auth.domain.dataobject.UserDO;
import com.atan.xiaohongshu.auth.domain.dataobject.UserRoleDO;
import com.atan.xiaohongshu.auth.domain.mapper.RoleDOMapper;
import com.atan.xiaohongshu.auth.domain.mapper.UserDOMapper;
import com.atan.xiaohongshu.auth.domain.mapper.UserRoleDOMapper;
import com.atan.xiaohongshu.auth.enums.LoginTypeEnum;
import com.atan.xiaohongshu.auth.enums.ResponseCodeEnum;
import com.atan.xiaohongshu.auth.model.vo.user.UserLoginReqVO;
import com.atan.xiaohongshu.auth.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private UserDOMapper userDOMapper;

    @Autowired
    private UserRoleDOMapper userRoleDOMapper;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Autowired
    private RoleDOMapper roleDOMapper;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 用户登录接口
     *
     * @param userLoginReqVO
     * @return
     */
    @Override
    public String loginAndRegister(UserLoginReqVO userLoginReqVO) {
        //获取手机号
        String phone = userLoginReqVO.getPhone();
        //获取验证码
        String code = userLoginReqVO.getCode();
        //获取密码
        String password = userLoginReqVO.getPassword();
        //获取类型
        Integer type = userLoginReqVO.getType();
        Long userId = null;
        //判断登录类型
        LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);
        //根据不同类型的枚举进行登录处理
        switch (Objects.requireNonNull(loginTypeEnum)) {
            case VERIFICATION_CODE: //验证码登录
                  //校验验证码是否为空
//                if(code.isBlank()){
//                    throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
//                }
                Preconditions.checkArgument(StringUtils.isNotBlank(code),"验证码不能为空");
                //校验验证码与redis中是否相符
                String verificationCodeKey = RedisConstants.getVerificationCodeKey(phone);
                String redisCode = (String) redisTemplate.opsForValue().get(verificationCodeKey);
                if(StringUtils.isNotBlank(redisCode) && !redisCode.equals(code)){
                    throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
                }
                //判断是否是新老用户(通过手机号查询数据库)
                UserDO userDO = userDOMapper.selectByPhone(phone);
                if(Objects.isNull(userDO)){
                    //新用户
                    userId = registerUser(phone);
                }else {
                    //老用户
                    userId = userDO.getId();
                }
                break;
            case PASSWORD: //密码登录
                //todo

                break;
            default:
                break;
        }
        // SaToken 登录用户, 入参为用户 ID
        StpUtil.login(userId);

        // 获取 Token 令牌
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        // 返回 Token 令牌
        return tokenInfo.tokenValue;


    }

    /**
     * 系统自动注册新用户
     * 为方法添加 @Transactional(rollbackFor = Exception.class) 事务注解，保证方法块内代码的原子性，要么全部成功，要么全部失败；
     * 使用声明式事务，这里是产生了自调用行为，会导致事务失效。故采用编程式事务
     * @param phone
     * @return
     */
//    @Transactional(rollbackFor = Exception.class)
    public Long registerUser(String phone) {
        return transactionTemplate.execute(status ->{
            try {
                //redis中手动放入了自增id 10000
                Long xiaohongshuId = redisTemplate.opsForValue().increment(RedisConstants.XIAOHONGSHU_ID_GENERATOR_KEY);
                //自动生成user
                UserDO userDO = UserDO.builder()
                        .phone(phone)
                        .xiaohongshuId(String.valueOf(xiaohongshuId)) // 自动生成小红书号 ID
                        .nickname("小红薯" + xiaohongshuId) // 自动生成昵称, 如：小红薯10000
                        .status(StatusEnum.ENABLED.getValue()) // 状态为启用
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .isDeleted(DeletedEnum.NO.getValue()) // 逻辑删除
                        .build();

                // 添加入库
                userDOMapper.insert(userDO);

                // 获取刚刚添加入库的用户 ID
                Long userId = userDO.getId();

                // 给该用户分配一个默认角色
                UserRoleDO userRoleDO = UserRoleDO.builder()
                        .userId(userId)
                        .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .isDeleted(DeletedEnum.NO.getValue())
                        .build();

                userRoleDOMapper.insert(userRoleDO);

                /**
                 * 由于网关层面的鉴权，需要拿到用户的权限和角色信息，
                 * 角色开始存储的是key为phone为标识，value为角色id
                 * 这里改成key 为userId为标识，value为roleKey
                 */
                // 将该用户的角色 ID 存入 Redis 中，指定初始容量为 1，这样可以减少在扩容时的性能开销
                List<String> roles = new ArrayList<>(1);
//                List<Long> roles = Lists.newArrayList();
//                roles.add(RoleConstants.COMMON_USER_ROLE_ID);
                RoleDO roleDO = roleDOMapper.selectByPrimaryKey(RoleConstants.COMMON_USER_ROLE_ID);
                roles.add(roleDO.getRoleKey());
                log.info(" {}  {}", roles, objectMapper.writeValueAsString(roles));
                String userRolesKey = RedisConstants.buildUserRoleKey(userId);
                //序列化和反序列化格式要一致
                redisTemplate.opsForValue().set(userRolesKey,roles);

                return userId;
            } catch (Exception e) {
                status.setRollbackOnly(); //标记为事务回滚
                log.error("==> 系统注册用户异常: ", e);
                return null;
            }
        });



    }
}
